#region Using References

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;

#endregion

namespace Beaker.OpenCube.Game
{
	/// <summary>
	/// The total block information, type and extra data.
	/// </summary>
	[DebuggerDisplay( "{Type}:{Data.Value}" )]
	public struct Block
		: IEquatable<Block>
	{
		#region Mask

		public static readonly IReadOnlyDictionary<BlockType, Nibble> BlockDataMasks =
			new ReadOnlyDictionary<BlockType, Nibble>(
				new Dictionary<BlockType, Nibble>
				{
					{ BlockType.Wood, (Nibble)0x3 },
					{ BlockType.WoodPlanks, (Nibble)0x3 },
					{ BlockType.Sapling, (Nibble)0x3 },
					{ BlockType.Leaves, (Nibble)0x3 },
					{ BlockType.Wool, (Nibble)0xf },
					{ BlockType.StoneSlab, (Nibble)0xf },
					{ BlockType.DoubleStoneSlabs, (Nibble)0xf },
					{ BlockType.WoodenSlab, (Nibble)0x3 },
					{ BlockType.Sandstone, (Nibble)0x3 },
					{ BlockType.MonsterEgg, (Nibble)0x3 },
					{ BlockType.StoneBricks, (Nibble)0x3 },
					{ BlockType.CobblestoneWall, (Nibble)0x1 },
					{ BlockType.QuartzBlock, (Nibble)0xf }
				}
			);

		#endregion
		#region Block

		private static readonly Dictionary<BlockType, List<Block>> _subBlockMapping =
			new Dictionary<BlockType, List<Block>>();
		public static readonly IReadOnlyDictionary<BlockType, List<Block>> SubBlockMapping =
			new ReadOnlyDictionary<BlockType, List<Block>>( _subBlockMapping );

		public static readonly Block Air = new Block( BlockType.Air );
		public static readonly Block Stone = new Block( BlockType.Stone );
		public static readonly Block Grass = new Block( BlockType.Grass );
		public static readonly Block Dirt = new Block( BlockType.Dirt );
		public static readonly Block Cobblestone = new Block( BlockType.Cobblestone );
		public static readonly Block WoodPlanksOak = new Block( BlockType.WoodPlanks, (Nibble)0, (Nibble)3 );
		public static readonly Block WoodPlanksSpruce = new Block( BlockType.WoodPlanks, (Nibble)1, (Nibble)3 );
		public static readonly Block WoodPlanksBirch = new Block( BlockType.WoodPlanks, (Nibble)2, (Nibble)3 );
		public static readonly Block WoodPlanksJungle = new Block( BlockType.WoodPlanks, (Nibble)3, (Nibble)3 );
		public static readonly Block SaplingOak = new Block( BlockType.Sapling, (Nibble)0, (Nibble)3 );
		public static readonly Block SaplingSpruce = new Block( BlockType.Sapling, (Nibble)1, (Nibble)3 );
		public static readonly Block SaplingBirch = new Block( BlockType.Sapling, (Nibble)2, (Nibble)3 );
		public static readonly Block SaplingJungle = new Block( BlockType.Sapling, (Nibble)3, (Nibble)3 );
		public static readonly Block Bedrock = new Block( BlockType.Bedrock );
		public static readonly Block Water = new Block( BlockType.Water );
		public static readonly Block StationaryWater = new Block( BlockType.StationaryWater );
		public static readonly Block Lava = new Block( BlockType.Lava );
		public static readonly Block StationaryLava = new Block( BlockType.StationaryLava );
		public static readonly Block Sand = new Block( BlockType.Sand );
		public static readonly Block Gravel = new Block( BlockType.Gravel );
		public static readonly Block GoldOre = new Block( BlockType.GoldOre );
		public static readonly Block IronOre = new Block( BlockType.IronOre );
		public static readonly Block CoalOre = new Block( BlockType.CoalOre );
		public static readonly Block WoodOak = new Block( BlockType.Wood, (Nibble)0, (Nibble)3 );
		public static readonly Block WoodSpruce = new Block( BlockType.Wood, (Nibble)1, (Nibble)3 );
		public static readonly Block WoodBirch = new Block( BlockType.Wood, (Nibble)2, (Nibble)3 );
		public static readonly Block WoodJungle = new Block( BlockType.Wood, (Nibble)3, (Nibble)3 );
		public static readonly Block LeavesOak = new Block( BlockType.Leaves, (Nibble)0, (Nibble)3 );
		public static readonly Block LeavesSpruce = new Block( BlockType.Leaves, (Nibble)1, (Nibble)3 );
		public static readonly Block LeavesBirch = new Block( BlockType.Leaves, (Nibble)2, (Nibble)3 );
		public static readonly Block LeavesJungle = new Block( BlockType.Leaves, (Nibble)3, (Nibble)3 );
		public static readonly Block Sponge = new Block( BlockType.Sponge );
		public static readonly Block Glass = new Block( BlockType.Glass );
		public static readonly Block LapisLazuliOre = new Block( BlockType.LapisLazuliOre );
		public static readonly Block LapisLazuliBlock = new Block( BlockType.LapisLazuliBlock );
		public static readonly Block Dispenser = new Block( BlockType.Dispenser );
		public static readonly Block SandstoneNormal = new Block( BlockType.Sandstone, (Nibble)0, (Nibble)3 );
		public static readonly Block SandstoneChiseled = new Block( BlockType.Sandstone, (Nibble)1, (Nibble)3 );
		public static readonly Block SandstoneSmooth = new Block( BlockType.Sandstone, (Nibble)2, (Nibble)3 );
		public static readonly Block NoteBlock = new Block( BlockType.NoteBlock );
		public static readonly Block BedHead = new Block( BlockType.Bed, (Nibble)8, (Nibble)8 );
		public static readonly Block BedFoot = new Block( BlockType.Bed, (Nibble)0, (Nibble)8 );
		public static readonly Block PoweredRail = new Block( BlockType.PoweredRail );
		public static readonly Block DetectorRail = new Block( BlockType.DetectorRail );
		public static readonly Block StickyPiston = new Block( BlockType.StickyPiston );
		public static readonly Block Cobweb = new Block( BlockType.Cobweb );
		public static readonly Block Scrub = new Block( BlockType.TallGrass, (Nibble)0, (Nibble)3 );
		public static readonly Block TallGrass = new Block( BlockType.TallGrass, (Nibble)1, (Nibble)3 );
		public static readonly Block Fern = new Block( BlockType.TallGrass, (Nibble)2, (Nibble)3 );
		public static readonly Block DeadBush = new Block( BlockType.DeadBush );
		public static readonly Block Piston = new Block( BlockType.Piston );
		public static readonly Block PistonExtension = new Block( BlockType.PistonExtension );
		public static readonly Block WoolWhite = new Block( BlockType.Wool, (Nibble)0, (Nibble)15 );
		public static readonly Block WoolOrange = new Block( BlockType.Wool, (Nibble)1, (Nibble)15 );
		public static readonly Block WoolMagenta = new Block( BlockType.Wool, (Nibble)2, (Nibble)15 );
		public static readonly Block WoolLightBlue = new Block( BlockType.Wool, (Nibble)3, (Nibble)15 );
		public static readonly Block WoolYellow = new Block( BlockType.Wool, (Nibble)4, (Nibble)15 );
		public static readonly Block WoolLime = new Block( BlockType.Wool, (Nibble)5, (Nibble)15 );
		public static readonly Block WoolPink = new Block( BlockType.Wool, (Nibble)6, (Nibble)15 );
		public static readonly Block WoolGray = new Block( BlockType.Wool, (Nibble)7, (Nibble)15 );
		public static readonly Block WoolLightGray = new Block( BlockType.Wool, (Nibble)8, (Nibble)15 );
		public static readonly Block WoolCyan = new Block( BlockType.Wool, (Nibble)9, (Nibble)15 );
		public static readonly Block WoolPurple = new Block( BlockType.Wool, (Nibble)10, (Nibble)15 );
		public static readonly Block WoolBlue = new Block( BlockType.Wool, (Nibble)11, (Nibble)15 );
		public static readonly Block WoolBrown = new Block( BlockType.Wool, (Nibble)12, (Nibble)15 );
		public static readonly Block WoolGreen = new Block( BlockType.Wool, (Nibble)13, (Nibble)15 );
		public static readonly Block WoolRed = new Block( BlockType.Wool, (Nibble)14, (Nibble)15 );
		public static readonly Block WoolBlack = new Block( BlockType.Wool, (Nibble)15, (Nibble)15 );
		public static readonly Block BlockMovedByPiston = new Block( BlockType.BlockMovedByPiston );
		public static readonly Block Dandelion = new Block( BlockType.Dandelion );
		public static readonly Block Rose = new Block( BlockType.Rose );
		public static readonly Block BrownMushroom = new Block( BlockType.BrownMushroom );
		public static readonly Block RedMushroom = new Block( BlockType.RedMushroom );
		public static readonly Block GoldBlock = new Block( BlockType.GoldBlock );
		public static readonly Block IronBlock = new Block( BlockType.IronBlock );
		public static readonly Block DoubleStoneSlabsStone = new Block( BlockType.DoubleStoneSlabs, (Nibble)0, (Nibble)7 );
		public static readonly Block DoubleStoneSlabsSandstone = new Block( BlockType.DoubleStoneSlabs, (Nibble)1, (Nibble)7 );
		public static readonly Block DoubleStoneSlabsWooden = new Block( BlockType.DoubleStoneSlabs, (Nibble)2, (Nibble)7 );
		public static readonly Block DoubleStoneSlabsCobblestone = new Block( BlockType.DoubleStoneSlabs, (Nibble)3, (Nibble)7 );
		public static readonly Block DoubleStoneSlabsBrick = new Block( BlockType.DoubleStoneSlabs, (Nibble)4, (Nibble)7 );
		public static readonly Block DoubleStoneSlabsStoneBrick = new Block( BlockType.DoubleStoneSlabs, (Nibble)5, (Nibble)7 );
		public static readonly Block DoubleStoneSlabsNetherBrick = new Block( BlockType.DoubleStoneSlabs, (Nibble)6, (Nibble)7 );
		public static readonly Block DoubleStoneSlabsQuartz = new Block( BlockType.DoubleStoneSlabs, (Nibble)7, (Nibble)7 );
		public static readonly Block StoneSlabsStone = new Block( BlockType.StoneSlab, (Nibble)0, (Nibble)7 );
		public static readonly Block StoneSlabsSandstone = new Block( BlockType.StoneSlab, (Nibble)1, (Nibble)7 );
		public static readonly Block StoneSlabsWooden = new Block( BlockType.StoneSlab, (Nibble)2, (Nibble)7 );
		public static readonly Block StoneSlabsCobblestone = new Block( BlockType.StoneSlab, (Nibble)3, (Nibble)7 );
		public static readonly Block StoneSlabsBrick = new Block( BlockType.StoneSlab, (Nibble)4, (Nibble)7 );
		public static readonly Block StoneSlabsStoneBrick = new Block( BlockType.StoneSlab, (Nibble)5, (Nibble)7 );
		public static readonly Block StoneSlabsNetherBrick = new Block( BlockType.StoneSlab, (Nibble)6, (Nibble)7 );
		public static readonly Block StoneSlabsQuartz = new Block( BlockType.StoneSlab, (Nibble)7, (Nibble)7 );
		public static readonly Block Bricks = new Block( BlockType.Bricks );
		public static readonly Block Tnt = new Block( BlockType.Tnt );
		public static readonly Block Bookshelf = new Block( BlockType.Bookshelf );
		public static readonly Block MossyCobblestone = new Block( BlockType.MossyCobblestone );
		public static readonly Block Obsidian = new Block( BlockType.Obsidian );
		public static readonly Block Torch = new Block( BlockType.Torch );
		public static readonly Block Fire = new Block( BlockType.Fire );
		public static readonly Block MonsterSpawner = new Block( BlockType.MonsterSpawner );
		public static readonly Block WoodenOakStairs = new Block( BlockType.WoodenOakStairs );
		public static readonly Block Chest = new Block( BlockType.Chest );
		public static readonly Block RedstoneWire = new Block( BlockType.RedstoneWire );
		public static readonly Block DiamondOre = new Block( BlockType.DiamondOre );
		public static readonly Block DiamondBlock = new Block( BlockType.DiamondBlock );
		public static readonly Block Wheat = new Block( BlockType.Wheat );
		public static readonly Block CraftingTable = new Block( BlockType.CraftingTable );
		public static readonly Block Farmland = new Block( BlockType.Farmland );
		public static readonly Block Furnace = new Block( BlockType.Furnace );
		public static readonly Block FurnaceBurning = new Block( BlockType.FurnaceBurning );
		public static readonly Block SignPost = new Block( BlockType.SignPost );
		public static readonly Block WoodenDoor = new Block( BlockType.WoodenDoor );
		public static readonly Block Ladder = new Block( BlockType.Ladder );
		public static readonly Block Rail = new Block( BlockType.Rail );
		public static readonly Block CobblestoneStairs = new Block( BlockType.CobblestoneStairs );
		public static readonly Block WallSign = new Block( BlockType.WallSign );
		public static readonly Block Lever = new Block( BlockType.Lever );
		public static readonly Block StonePressurePlate = new Block( BlockType.StonePressurePlate );
		public static readonly Block IronDoor = new Block( BlockType.IronDoor );
		public static readonly Block WoodenPressurePlate = new Block( BlockType.WoodenPressurePlate );
		public static readonly Block RedstoneOre = new Block( BlockType.RedstoneOre );
		public static readonly Block RedstoneOreGlowing = new Block( BlockType.RedstoneOreGlowing );
		public static readonly Block RedstoneTorchActive = new Block( BlockType.RedstoneTorchActive );
		public static readonly Block RedstoneTorchInactive = new Block( BlockType.RedstoneTorchInactive );
		public static readonly Block StoneButton = new Block( BlockType.StoneButton );
		public static readonly Block SnowDeck = new Block( BlockType.SnowDeck );
		public static readonly Block IceBlock = new Block( BlockType.IceBlock );
		public static readonly Block SnowBlock = new Block( BlockType.SnowBlock );
		public static readonly Block Cactus = new Block( BlockType.Cactus );
		public static readonly Block ClayBlock = new Block( BlockType.ClayBlock );
		public static readonly Block SugarCane = new Block( BlockType.SugarCane );
		public static readonly Block Jukebox = new Block( BlockType.Jukebox );
		public static readonly Block Fence = new Block( BlockType.Fence );
		public static readonly Block Pumpkin = new Block( BlockType.Pumpkin );
		public static readonly Block Netherrack = new Block( BlockType.Netherrack );
		public static readonly Block SoulSand = new Block( BlockType.SoulSand );
		public static readonly Block GlowstoneBlock = new Block( BlockType.GlowstoneBlock );
		public static readonly Block NetherPortal = new Block( BlockType.NetherPortal );
		public static readonly Block JackOLantern = new Block( BlockType.JackOLantern );
		public static readonly Block CakeBlock = new Block( BlockType.CakeBlock );
		public static readonly Block RedstoneRepeaterActive = new Block( BlockType.RedstoneRepeaterActive );
		public static readonly Block RedstoneRepeaterInactive = new Block( BlockType.RedstoneRepeaterInactive );
		public static readonly Block LockedChest = new Block( BlockType.LockedChest );
		public static readonly Block Trapdoor = new Block( BlockType.Trapdoor );
		public static readonly Block MonsterEggStone = new Block( BlockType.MonsterEgg, (Nibble)0, (Nibble)3 );
		public static readonly Block MonsterEggCobblestone = new Block( BlockType.MonsterEgg, (Nibble)1, (Nibble)3 );
		public static readonly Block MonsterEggCracked = new Block( BlockType.MonsterEgg, (Nibble)2, (Nibble)3 );
		public static readonly Block StoneBricksNormal = new Block( BlockType.StoneBricks, (Nibble)0, (Nibble)3 );
		public static readonly Block StoneBricksMossy = new Block( BlockType.StoneBricks, (Nibble)1, (Nibble)3 );
		public static readonly Block StoneBricksCracked = new Block( BlockType.StoneBricks, (Nibble)2, (Nibble)3 );
		public static readonly Block StoneBricksChiseled = new Block( BlockType.StoneBricks, (Nibble)3, (Nibble)3 );
		public static readonly Block HugeBrownMushroom = new Block( BlockType.HugeBrownMushroom ); //? make 13 different blocks?
		public static readonly Block HugeRedMushroom = new Block( BlockType.HugeRedMushroom ); //? make 13 different blocks?
		public static readonly Block IronBars = new Block( BlockType.IronBars );
		public static readonly Block GlassPane = new Block( BlockType.GlassPane );
		public static readonly Block Melon = new Block( BlockType.Melon );
		public static readonly Block PumpkinStem = new Block( BlockType.PumpkinStem );
		public static readonly Block MelonStem = new Block( BlockType.MelonStem );
		public static readonly Block Vines = new Block( BlockType.Vines );
		public static readonly Block FenceGate = new Block( BlockType.FenceGate );
		public static readonly Block BrickStairs = new Block( BlockType.BrickStairs );
		public static readonly Block StoneBrickStairs = new Block( BlockType.StoneBrickStairs );
		public static readonly Block Mycelium = new Block( BlockType.Mycelium );
		public static readonly Block LilyPad = new Block( BlockType.LilyPad );
		public static readonly Block NetherBricks = new Block( BlockType.NetherBricks );
		public static readonly Block NetherBricksFence = new Block( BlockType.NetherBricksFence );
		public static readonly Block NetherBricksStairs = new Block( BlockType.NetherBricksStairs );
		public static readonly Block NetherWart = new Block( BlockType.NetherWart );
		public static readonly Block EnchantmentTable = new Block( BlockType.EnchantmentTable );
		public static readonly Block BrewingStand = new Block( BlockType.BrewingStand );
		public static readonly Block Cauldron = new Block( BlockType.Cauldron );
		public static readonly Block EndPortal = new Block( BlockType.EndPortal );
		public static readonly Block EndPortalBlock = new Block( BlockType.EndPortalBlock );
		public static readonly Block EndStone = new Block( BlockType.EndStone );
		public static readonly Block DragonEgg = new Block( BlockType.DragonEgg );
		public static readonly Block RedstoneLampActive = new Block( BlockType.RedstoneLampActive );
		public static readonly Block RedstoneLampInactive = new Block( BlockType.RedstoneLampInactive );
		public static readonly Block DoubleWoodenSlabsOak = new Block( BlockType.DoubleWoodenSlabs, (Nibble)0, (Nibble)3 );
		public static readonly Block DoubleWoodenSlabsSpruce = new Block( BlockType.DoubleWoodenSlabs, (Nibble)1, (Nibble)3 );
		public static readonly Block DoubleWoodenSlabsBirch = new Block( BlockType.DoubleWoodenSlabs, (Nibble)2, (Nibble)3 );
		public static readonly Block DoubleWoodenSlabsJungle = new Block( BlockType.DoubleWoodenSlabs, (Nibble)3, (Nibble)3 );
		public static readonly Block WoodenSlabOak = new Block( BlockType.WoodenSlab, (Nibble)0, (Nibble)3 );
		public static readonly Block WoodenSlabSpruce = new Block( BlockType.WoodenSlab, (Nibble)1, (Nibble)3 );
		public static readonly Block WoodenSlabBirch = new Block( BlockType.WoodenSlab, (Nibble)2, (Nibble)3 );
		public static readonly Block WoodenSlabJungle = new Block( BlockType.WoodenSlab, (Nibble)3, (Nibble)3 );
		public static readonly Block CocoaPod = new Block( BlockType.CocoaPod );
		public static readonly Block SandstoneStairs = new Block( BlockType.SandstoneStairs );
		public static readonly Block EmeraldOre = new Block( BlockType.EmeraldOre );
		public static readonly Block EnderChest = new Block( BlockType.EnderChest );
		public static readonly Block TripwireHook = new Block( BlockType.TripwireHook );
		public static readonly Block Tripwire = new Block( BlockType.Tripwire );
		public static readonly Block EmeraldBlock = new Block( BlockType.EmeraldBlock );
		public static readonly Block SpruceWoodStairs = new Block( BlockType.SpruceWoodStairs );
		public static readonly Block BirchWoodStairs = new Block( BlockType.BirchWoodStairs );
		public static readonly Block JungleWoodStairs = new Block( BlockType.JungleWoodStairs );
		public static readonly Block CommandBlock = new Block( BlockType.CommandBlock );
		public static readonly Block Beacon = new Block( BlockType.Beacon );
		public static readonly Block CobblestoneWallNormal = new Block( BlockType.CobblestoneWall );
		public static readonly Block CobblestoneWallMossy = new Block( BlockType.CobblestoneWall );
		public static readonly Block FlowerPot = new Block( BlockType.FlowerPot );
		public static readonly Block Carrots = new Block( BlockType.Carrots );
		public static readonly Block Potatoes = new Block( BlockType.Potatoes );
		public static readonly Block WoodenButton = new Block( BlockType.WoodenButton );
		public static readonly Block MobHead = new Block( BlockType.MobHead );
		public static readonly Block Anvil = new Block( BlockType.Anvil );
		public static readonly Block TrappedChest = new Block( BlockType.TrappedChest );
		public static readonly Block WeightedPressurePlateLight = new Block( BlockType.WeightedPressurePlateLight );
		public static readonly Block WeightedPressurePlateHeavy = new Block( BlockType.WeightedPressurePlateHeavy );
		public static readonly Block RedstoneComparatorActive = new Block( BlockType.RedstoneComparatorActive );
		public static readonly Block RedstoneComparatorInactive = new Block( BlockType.RedstoneComparatorInactive );
		public static readonly Block DaylightSensor = new Block( BlockType.DaylightSensor );
		public static readonly Block RedstoneBlock = new Block( BlockType.RedstoneBlock );
		public static readonly Block NetherQuartzOre = new Block( BlockType.NetherQuartzOre );
		public static readonly Block Hopper = new Block( BlockType.Hopper );
		public static readonly Block QuartzBlock = new Block( BlockType.QuartzBlock );
		public static readonly Block QuartzStairs = new Block( BlockType.QuartzStairs );
		public static readonly Block ActivatorRail = new Block( BlockType.ActivatorRail );
		public static readonly Block Dropper = new Block( BlockType.Dropper );
		public static readonly Block HayBlock = new Block( BlockType.HayBlock );
		public static readonly Block Carpet = new Block( BlockType.Carpet );

		#endregion
		private readonly BlockType _blockType;
		private readonly Nibble _blockData;
		private readonly Nibble _dataMask;

		/// <summary>
		/// Initializes a new instance of the <see cref="Block" /> class.
		/// </summary>
		/// <param name="blockType">Type of the block.</param>
		private Block( BlockType blockType )
		{
			Contract.Requires<ArgumentOutOfRangeException>( Enum.IsDefined( typeof( BlockType ), blockType ) );
			Contract.Requires<ArgumentOutOfRangeException>( blockType != BlockType.Unknown );
			Contract.Ensures( Contract.ValueAtReturn( out _blockType ) == blockType );
			Contract.Ensures( Contract.ValueAtReturn( out _blockData ) == Nibble.Zero );
			Contract.Ensures( Contract.ValueAtReturn( out _dataMask ) == Nibble.Zero );

			_blockType = blockType;
			_blockData = Nibble.Zero;
			_dataMask = Nibble.Zero;

			RegisterBlock( blockType, this );
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="Block" /> class.
		/// </summary>
		/// <param name="blockType">Type of the block.</param>
		/// <param name="blockData">The block data.</param>
		/// <param name="dataMask">The block data filter.</param>
		private Block( BlockType blockType, Nibble blockData, Nibble dataMask )
		{
			Contract.Requires<ArgumentOutOfRangeException>( Enum.IsDefined( typeof( BlockType ), blockType ) );
			Contract.Requires<ArgumentOutOfRangeException>( blockType != BlockType.Unknown );
			Contract.Ensures( Contract.ValueAtReturn( out _blockType ) == blockType );
			Contract.Ensures( Contract.ValueAtReturn( out _blockData ) == blockData );
			Contract.Ensures( Contract.ValueAtReturn( out _dataMask ) == dataMask );

			_blockType = blockType;
			_blockData = blockData;
			_dataMask = dataMask;

			RegisterBlock( blockType, this );
		}

		/// <summary>
		/// Registers the block in the dictionary.
		/// </summary>
		/// <param name="type">The main type of the block.</param>
		/// <param name="blockToRegister">The block to register.</param>
		private static void RegisterBlock( BlockType type, Block blockToRegister )
		{
			List<Block> list;
			if( !_subBlockMapping.TryGetValue( type, out list ) )
			{
				list = new List<Block>();
				_subBlockMapping.Add( type, list );
			}

			list.Add( blockToRegister );
		}

		[ContractInvariantMethod]
		private void ContractInvariant()
		{
			Contract.Invariant( Enum.IsDefined( typeof( BlockType ), _blockType ) );
			Contract.Invariant( _blockType != BlockType.Unknown );
		}

		/// <summary>
		/// Gets the type of the block.
		/// </summary>
		/// <value>
		/// The type of the block.
		/// </value>
		public BlockType Type
		{
			get
			{
				Contract.Ensures( Enum.IsDefined( typeof( BlockType ), Contract.Result<BlockType>() ) );
				Contract.Ensures( Contract.Result<BlockType>() != BlockType.Unknown );
				return _blockType;
			}
		}

		/// <summary>
		/// Gets the additional block data
		/// </summary>
		/// <value>
		/// The data.
		/// </value>
		public Nibble Data
		{
			get { return _blockData; }
		}

		/// <summary>
		/// Gets the data mask.
		/// </summary>
		/// <value>
		/// The data mask.
		/// </value>
		public Nibble DataMask
		{
			get { return _dataMask; }
		}

		#region Equals

		public override bool Equals( object obj )
		{
			if( ReferenceEquals( null, obj ) ) return false;
			return obj is Block && Equals( (Block)obj );
		}

		/// <summary>
		/// Indicates whether the current block is equal to the other block.
		/// </summary>
		/// <returns>
		/// <c>true</c> if the current block is equal to the <paramref name="other"/> block; otherwise, <c>false</c>.
		/// </returns>
		/// <param name="other">A block to compare with this block.</param>
		public bool Equals( Block other )
		{
			return _blockType.Equals( other._blockType )
				&& ( _blockData & _dataMask ).Equals( other._blockData & other._dataMask );
		}

		public static bool operator ==( Block left, Block right )
		{
			return left.Equals( right );
		}

		public static bool operator !=( Block left, Block right )
		{
			return !left.Equals( right );
		}

		#endregion

		/// <summary>
		/// Returns the hash code for this instance.
		/// </summary>
		/// <returns>
		/// A 32-bit signed integer that is the hash code for this instance.
		/// </returns>
		public override int GetHashCode()
		{
			unchecked
			{
				return ( _blockType.GetHashCode() * 397 ) ^ _blockData.GetHashCode();
			}
		}

		public static Block Get( BlockType type, Nibble data )
		{
			List<Block> list;
			if( !_subBlockMapping.TryGetValue( type, out list ) )
				throw new KeyNotFoundException();

			return list.SingleOrDefault(b => b.Data == ( data & b.DataMask ));
		}
	}
}